// let num:number = 1
// let str:string = 'thc'
// let isVail:boolean = true
// let varValue:any = 'typescript'

// //指定返回值类型为number
// function sum(a:number, b:number):number{
//     return a+b
// }
// //void表示没有返回值 若强行return一个非空的值，会报错
// function fn():void{
  
// }
// //never 表示永远不会返回结果,常用来报错
// function fnNever():never{
//   throw new Error('报错了')
// }

let lambda:unknown = 'thc'  //unknown实际上就是一个类型安全的any，但不能直接赋值给其他变量
lambda = '我爱打水'
// str = lambda  //  如果直接赋值，这回报错。如果要进行赋值操作，可以通过类型断言
// str = lambda as string  //类型断言: 通过as来告诉解析器变量的实际类型
// str = <string> lambda  //类型断言的另一种写法

// /**     
//  * 类型断言补充
//  *  如果某个变量确定不可能为null或undefined,可以在变量后通过！修饰
//  */
// function dealNum(num?:number|null){
//     console.log(typeof num!.toFixed(2))
// }


// /*
// *   定义对象
// *   通过{}来指定对象中可以包含哪些属性
// *   语法： {属性名：属性值，属性名：属性名}
// *   其中属性后跟 ？表示可选，在实例化时可选
// *   一一(通过？)列举不可取，可通过 [propName:string]:any来表示任意类型的属性，如此就不需要一一列举了
// *   propName表示随便值，相当于变量属性名，可以是其他比如 a、b、c等等
// */
// let obj: {name:string, sex?:string, [propName:string]:any}
// obj = {
//     name: 'thc',
//     age:26  //上行声明obj时没有列举age,此处也没有报错，原因是设置了 [propName:string]:any
// }

// /**
//  * 定义函数
//  * 语法： functionName: (参数名:参数类型，参数名：参数类型)=> 返回参数类型 
//  */
// let sumFn:(a:number,b:number)=>number
// sumFn = (a,b)=>{
//     return a+b
// }

// /**
//  * 定义数组
//  * 语法：变量名：类型[]或 Array<类型>
//  */
// let arr:number[]
// arr = [1,2]
// let list:Array<string>
// list = [
//     'thc',
//     '我爱打水'
// ]

// /**
//  * 新类型 tuple（元组）：固定长度的数组，不可少也不可多
//  *        enum（枚举）：列举所有的可能
//  */
let tupleValue:[string, number]
tupleValue = ['hello', 100]

// enum statuslist{
//     run,
//     warn=3,
//     stop      
// }
// /**
//  * 若设置了warn=3 则stop的值为4，但不影响run的值，run的值还是0
//  */


// // 类型的别名: 给类型起个别名，后续可通过别名进行限定变量属性的类型
// type myStr = string
// let account:myStr = 'thc'



function prop<T,K extends keyof T>(obj:T, key:K){
    return obj[key]
}
let o = {
    a:1,
    b:2,
    c:3
}
prop(o, 'a')

let list1:number[] = [2,3]
let list2: Array<number> = [2,3,4]
let list3:Array<number|string> = ['thc', 2]

//定义只读对象
let obj1:object = {
    x:1,
    y:3
}
// obj1.x = 2  只读对象，不可重新赋值

//定义可读写对象
let obj2: {x:number,y:string} = {
    x:2,
    y: 'sex'
}
obj2.x = 3


//定义函数
const noReturn = ():void => {
    console.log('no return value')
}
let add = (x:number,y:number):number => x+y

//定义函数类型，但没有实现它
let plus:(x:number, y:number) => number
// 实现
plus = (a,b) => a+b
//调用
plus(2,3)

let a:number;  //这叫声明
let a2:number = 2; //这叫定义

interface HeroPerson{
    name: string,
    age: number
}
let man:HeroPerson = {
    name: 'sb',
    age: 250
}

interface workInfo{
    id: string,
    name: string,
    [x:string]: any
}
let shopper:workInfo = {
    id: '467841',
    name: '购物员',
    num: 20
}

//任意长度的数组
interface StringArray{
    [i:number]:string
}
let chart:StringArray = ['a', 'b']


interface List{
    readonly id: number,
    name: string,
    age?:number
}
interface Result{
    data: Array<List>
}
const render = (result:Result):void=>{
    console.log(JSON.stringify(result))
}
//将对象字面当做参数传给Render函数时，编译器会对对象内的属性进行检查
// 需要通过类型断言来规避这个问题

//类型断言方式一
render({
    data: [
        {
            id:1,
            name:'s',
            sex:'male'
        },
        {
            id:2,
            name: 'b'
        }
    ]
} as Result)   

//类型断言方式二
render(<Result>{
    data: [
        {
            id:1,
            name:'s',
            sex: 'male'
        },
        {
            id:2,
            name:'b'
        }
    ]
})
//函数类型接口

// 声明一个函数类型
let sum:(a:number,b:number)=>number
// const hs = (a:number,b:number):number=>{
//     return a+b
// }
//实现
sum = (a,b)=>{
    return a+b
}

//通过接口来定义函数类型
interface sum{
    (x:number,y:number):number
}
let sumRes:sum = (a,b)=>a+b

//定义函数的四种方式
function result1(x:number, y:number){
    return x+y
}
let result2:(x:number,y:number)=>number
type result3 = (x:number,y:number)=>number
interface result4{
    (x:number,y:number):number
}

//默认参数
function asFn(a:number, b:number=3):number{
    return a+b
}
asFn(3)
//剩余参数
function restFn(x:number, ...last:Array<number>){
    return x+ last.reduce((prev,cur)=> prev+cur)
}
restFn(1, 2, 3, 4)

//es5写法
function Point1(x,y){
    this.x = x;
    this.y = y;
}
Point1.prototype.toString = function(){
    return `(${this.x},${this.y})`
}
let p = new Point1(1,2) 

//es6写法
class Point2{
    x:number
    y:number
    constructor(x:number,y:number){
        this.x = x
        this.y = y
    }
    toString():string{
        return  `(${this.x},${this.y})`
    }
}

//抽象类
abstract class Animal{
    public name:string
    public constructor(name: string){
        this.name = name
    }
    abstract sayHi():any
}

//抽象类不允许被实例化
// let dog = new Animal('狗')

class Cat extends Animal{
    public color:string
    constructor(name:string, color:string){
        super(name)
        this.color = color
    }
    //抽象类中的抽象方法必须被继承类实现
    sayHi(){
        console.log('喵')
    }
}
let myCat = new Cat('mimo', 'blue')
myCat.sayHi()

interface Animal_{
    name: string
    eat():void
}
class Cat_ implements Animal_{
    name: string
    constructor(name:string){
        this.name = name
    }
    eat(): void {
        console.log('cat eat fish') 
    }
}
/**
 * 泛型
 * 是指在定义函数、接口或类的时候，不预先指定具体的类型，而在使用的时候再指定类型的一种特性
 * 小技巧：直接把泛型理解为代表类型的参数
 */
interface lengthWise{
    length: number
}
function loggingIdentity<T extends lengthWise>(arg:T):T{
    console.log(arg.length)
    return arg
}
//用泛型来约束函数的参数和返回类型
type Log = <T>(value:T)=>T
let log:Log = (value)=>{
    console.log(value)
    return value
}
log<number>(2)
log('2')
log(true)

interface CreateArrayFunc{
    <T>(length:number,value:T):Array<T>
}

let createArray:CreateArrayFunc
createArray = <T>(length:number,value:T):Array<T>=>{
    let result:T[] = []
    for(let i=0; i<length; i++){
        result[i] = value
    }
    return result
}

//泛型类
class Log_<T>{
    run(value:T):T{
        console.log(value)
        return value
    }
}
let log_1 = new Log_<number>()
log_1.run(1)  //1

// 交叉类型
interface DogInterface{
    run():void
}
interface CatInterface{
    jump():void
}
let pet:DogInterface&CatInterface = {
    run():void{},
    jump():void{}
}
